package com.wutianyi.web.wrapper;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;

public class MyHttpSession implements HttpSession, Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -7067579405317033981L;

	private static int DEFAULT_TIME_OUT = 30 * 60;

	static int bitIndexCounter = 0;

	private static final int ID_BIT_MASK = 1 << bitIndexCounter++;
	private static final int LAST_ACCESSED_TIME_BIT_MASK = 1 << bitIndexCounter++;
	private static final int CREATION_TIME_BIT_MASK = 1 << bitIndexCounter++;
	private static final int TIME_OUT_BIT_MASK = 1 << bitIndexCounter++;
	private static final int ATTRIBUTES_BIT_MASK = 1 << bitIndexCounter++;

	private transient String sessionId;
	private transient long lastAccessedTime;
	private transient long creationTime;
	private transient boolean isNew;
	private transient int timeout = DEFAULT_TIME_OUT;
	private transient Map<String, Object> attributes = new HashMap<String, Object>();

	public MyHttpSession(String sessionId) {
		this.sessionId = sessionId;
		this.isNew = true;
		creationTime = System.currentTimeMillis();
		lastAccessedTime = creationTime;
	}

	@Override
	public long getCreationTime() {
		return creationTime;
	}

	@Override
	public String getId() {
		return sessionId;
	}

	@Override
	public long getLastAccessedTime() {
		return lastAccessedTime;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.servlet.http.HttpSession#getServletContext()
	 */
	@Deprecated
	@Override
	public ServletContext getServletContext() {

		return null;
	}

	@Override
	public void setMaxInactiveInterval(int interval) {
		if (interval > DEFAULT_TIME_OUT) {
			timeout = DEFAULT_TIME_OUT;
		} else {
			timeout = interval;
		}
	}

	@Override
	public int getMaxInactiveInterval() {
		return timeout;
	}

	@Override
	public HttpSessionContext getSessionContext() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object getAttribute(String name) {
		return attributes.get(name);
	}

	@Override
	public Object getValue(String name) {
		return getAttribute(name);
	}

	@Override
	public Enumeration<String> getAttributeNames() {
		final Iterator<String> iterable = attributes.keySet().iterator();
		return new Enumeration<String>() {
			@Override
			public boolean hasMoreElements() {
				return iterable.hasNext();
			}

			@Override
			public String nextElement() {
				if (iterable.hasNext()) {
					return iterable.next();
				}
				return null;
			}
		};
	}

	@Override
	public String[] getValueNames() {

		return null;
	}

	@Override
	public void setAttribute(String name, Object value) {
		attributes.put(name, value);
		lastAccessedTime = System.currentTimeMillis();
	}

	@Override
	public void putValue(String name, Object value) {
		setAttribute(name, value);
	}

	@Override
	public void removeAttribute(String name) {
		attributes.remove(name);
	}

	@Override
	public void removeValue(String name) {
		removeAttribute(name);

	}
	@Override
	public void invalidate() {
		attributes.clear();
	}

	@Override
	public boolean isNew() {
		return isNew;
	}

	@SuppressWarnings("unchecked")
	private void readObject(ObjectInputStream in) throws IOException,
			ClassNotFoundException {
		in.defaultReadObject();
		int bitMask = in.readInt();
		sessionId = (String) in.readObject();
		creationTime = in.readLong();
		if ((bitMask & LAST_ACCESSED_TIME_BIT_MASK) != 0) {
			lastAccessedTime = in.readLong();
		}
		timeout = in.readInt();
		if ((bitMask & ATTRIBUTES_BIT_MASK) != 0) {
			attributes = (Map<String, Object>) in.readObject();
		} else {
			attributes = new HashMap<String, Object>();
		}
		isNew = false;
		lastAccessedTime = creationTime;
	}

	private void writeObject(ObjectOutputStream out) throws IOException {
		out.defaultWriteObject();
		int bitMask = getAlterBitMask();
		boolean expire = System.currentTimeMillis() - creationTime > timeout;
		out.writeInt(bitMask);
		out.writeObject(sessionId);
		out.writeLong(creationTime);
		if (!expire && attributes.size() > 0) {
			out.writeLong(lastAccessedTime);
		}
		out.writeInt(timeout);
		if (!expire && attributes.size() > 0) {
			out.writeObject(attributes);
		}
	}

	private int getAlterBitMask() {
		int bitMask = 1;
		bitMask |= CREATION_TIME_BIT_MASK;
		boolean expire = System.currentTimeMillis() - creationTime > timeout;
		if (!expire && attributes.size() > 0) {
			bitMask |= LAST_ACCESSED_TIME_BIT_MASK;
		}

		bitMask |= TIME_OUT_BIT_MASK;

		if (!expire && attributes.size() > 0) {
			bitMask |= ATTRIBUTES_BIT_MASK;
		}
		return bitMask;
	}
}
